package game;

import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control;
import data.TankDesign;

public class TankControl extends AbstractControl{

    private RigidBodyControl physicsControl;
    
    private TankBodyControl body;
    private TankTurretControl turret;
    private TankTreadControl tread;
    
    private float acceleration;

    /**
     * Constructor for if the tank itself is a Geometry, rather than a Node with
     * geometry children in the scene graph. Use this only for testing purposes.
     * @param s - The Spatial to be given to the Tank.
     */
    public TankControl(Spatial s) {
        super();
        //CollisionShape collisionShape = new BoxCollisionShape(
         //       new Vector3f(5f,3f,1f));
        //physicsControl = new RigidBodyControl(collisionShape);
        //spatial.addControl(physicsControl);
         
    }
    
    /**
     * The constructor to be used in the game. To create a tank, you need to
     * supply a Node which the parts of the tank will attach to, and a 
     * TankDesign that tells what parts to use. The constructor will create
     * the Spatials for the TankParts, add their controls, and attach them to
     * the given Node.
     */
    public TankControl(Node n, TankDesign design) {
        
    }

    /**
     * The following methods are notification events. They are called when user input
     * associated with the movement the method describes is processed, e.g. goForward()
     * is called when the user presses the W key or up arrow key for a player-controlled
     * tank. These methods can also be called by AI algorithms for computer-controlled tanks
     * or network messages for enemy tanks in multiplayer.
     * 
     * In addition, goInRelativeDirection() allows you to provide a vector in
     * the XZ-plane and have it move in that direction. This allows easy mapping
     * from the analog stick to precise movement of the tank. We may do away
     * with the goForward() and similar methods and use this for all input, to
     * make things consistent. For now, this shows how to easily process input.
     */

    /**
     * Notification that the tank should go forward.
     */
    public void goForward(float tpf) {
        //TODO: This method is not optimized.
        
        //First, create the vector that stands for "forward"
        Vector3f forward = Vector3f.UNIT_X;
        //Then, rotate this vector to align the tank's "forward"
        Quaternion rotation = this.spatial.getWorldRotation();
        forward = rotation.mult(forward);
        //Multiply the unit vector by the tank's acceleration to determine force
        Vector3f force = forward.mult(this.acceleration*tpf);
        this.physicsControl.applyCentralForce(force);

    }

    /**
     * Notification that the tank should go backwards.
     */
    public void goBackward(float tpf) {
        //TODO: This method is not optimized.
        
        //First, create the vector that stands for "backward"
        Vector3f backward = Vector3f.UNIT_X.negate();
        //Then, rotate this vector to align the tank's "backward"
        Quaternion rotation = this.spatial.getWorldRotation();
        backward = rotation.mult(backward);
        //Multiply the unit vector by the tank's acceleration to determine force
        Vector3f force = backward.mult(this.acceleration*tpf);
        this.physicsControl.applyCentralForce(force);

    }

    /**
     * Notification that the tank should go left
     */
    public void goLeft(float tpf) {
        //TODO: This method is not optimized.
        
        //First, create the vector that stands for "left"
        Vector3f left = Vector3f.UNIT_Z.negate();
        //Then, rotate this vector to align the tank's "left"
        Quaternion rotation = this.spatial.getWorldRotation();
        left = rotation.mult(left);
        //Multiply the unit vector by the tank's acceleration to determine force
        Vector3f force = left.mult(this.acceleration*tpf);
        this.physicsControl.applyCentralForce(force);
    }

    /**
     * Notification that the tank should go right
     */
    public void goRight(float tpf) {
        //TODO: This method is not optimized.
        
        //First, create the vector that stands for "right"
        Vector3f right = Vector3f.UNIT_Z;
        //Then, rotate this vector to align the tank's "right"
        Quaternion rotation = this.spatial.getWorldRotation();
        right = rotation.mult(right);
        //Multiply the unit vector by the tank's acceleration to determine force
        Vector3f force = right.mult(this.acceleration*tpf);
        this.physicsControl.applyCentralForce(force);

    }
    
    /**
     * Notification that the tank should go in a certain direction.
     * This is useful for analog sticks, as opposed to digital keyboards.
     * The vector should be only in the XZ-plane for proper behavior. The vector
     * is RELATIVE, so the X-direction is treated as "forward" and the Z-
     * direction is treated as "right."
     * If the magnitude of the vector is 1, result is same as keyboard input
     * If the magnitude of the vector is less than 1, the tank will move more
     * gently. This lets you map analog stick data directly to this function,
     * creating a 1-to-1 control scheme.
     * @param direction - The direction vector to move in.
     */
    public void goInRelativeDirection(Vector3f direction, float tpf) {
        //TODO: This method is not optimized.
      
        //Rotate this vector to align to the tank's orientation
        Quaternion rotation = this.spatial.getWorldRotation();
        direction = rotation.mult(direction);
        //Multiply the vector by the tank's acceleration to determine force
        Vector3f force = direction.mult(this.acceleration*tpf);
        this.physicsControl.applyCentralForce(force);

    }

    /**
     * Notification that the tank should "turn," i.e. orient the camera differently
     * @param amount - The number of degrees the tank should turn by (positive is left,
     * negative is right)
     */
    public void turn(float amount) {
       
    }
    
    /**
     * Notification that the tank should try to shoot its weapon. If the weapon
     * is unavailable for any reason (e.g. there's a cooldown time), nothing
     * happens.
     */
    public void tryShoot() {
        
    }

    @Override
    protected void controlUpdate(float tpf) {
        
    }

    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {
        
    }

    public Control cloneForSpatial(Spatial spatial) {
        throw new UnsupportedOperationException("Not implemented yet");
    }


}
